﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using OpenPasteSpider.bind;
using OpenPasteSpider.monitor;
using OpenPasteSpider.monitormodel;
using Volo.Abp.DependencyInjection;
using Volo.Abp.ObjectMapping;

namespace OpenPasteSpider
{
    /// <summary>
    /// 兼容容器运行状态和资源消耗情况，及时同步容器的实际情况到数据库中... .. .
    /// </summary>
    public class StatusTaskHandler : ISingletonDependency
    {
        /// <summary>
        /// 
        /// </summary>
        //private System.Timers.Timer timerticker;

        /// <summary>
        /// 
        /// </summary>
        private ILogger<StatusTaskHandler> _logger;

        /// <summary>
        /// 
        /// </summary>
        private SpiderConfig _config;

        /// <summary>
        /// 
        /// </summary>
        private IObjectMapper _objectMapper;

        /// <summary>
        /// 
        /// </summary>
        public IServiceProvider _service;

        /// <summary>
        /// 
        /// </summary>
        private ActionHelper _actionHelper;

        /// <summary>
        /// 
        /// </summary>
        private ChannelHelper _channelHelper;

        /// <summary>
        /// 
        /// </summary>
        private SemaphoreSlim _semplorestate;

        /// <summary>
        /// 
        /// </summary>
        private readonly PublicModelHelper _modelHelper;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="logger"></param>
        /// <param name="objectMapper"></param>
        /// <param name="channelHelper"></param>
        /// <param name="config"></param>
        /// <param name="modelHelper"></param>
        /// <param name="service"></param>
        /// <param name="command"></param>
        public StatusTaskHandler(
            ILogger<StatusTaskHandler> logger,
            IObjectMapper objectMapper,
            ChannelHelper channelHelper,
            IOptions<SpiderConfig> config,
            PublicModelHelper modelHelper,
            IServiceProvider service,
            ActionHelper command)
        {
            _service = service;
            _actionHelper = command;
            _logger = logger;
            _channelHelper = channelHelper;
            _objectMapper = objectMapper;
            _config = config.Value;
            _semplorestate = new SemaphoreSlim(1);
            _modelHelper = modelHelper;

            _dichourcontainercollect = new Dictionary<string, DockerTimeStateDto>();

            _dichourlinuxcollect = new Dictionary<string, LinuxTimeStateDto>();

            linuxs = new List<int>();
        }

        /// <summary>
        /// 
        /// </summary>
        public void Dispose()
        {

        }


        private int runtickindex = 0;

        /// <summary>
        /// 
        /// </summary>
        private List<int> linuxs;
        /// <summary>
        /// 集群模式，当前节点的名称
        /// </summary>
        public string MySlaveName = "";

        /// <summary>
        /// 重新读取linux对象列表
        /// </summary>
        /// <param name="slavename"></param>
        /// <returns></returns>
        public async Task<bool> LinuxChangeAsync(SlaveEventCode code, string slavename)
        {
            try
            {
                this.MySlaveName = slavename;
                linuxs.Clear();
            }
            finally
            {
                await Task.Delay(100);
            }
            return true;

        }

        /// <summary>
        /// 每隔300秒进行数据监测
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public async void DoReadStatsAsync(object sender, System.Timers.ElapsedEventArgs e)
        {
            runtickindex++;
            //if (!isRunning)
            //{
            //    isRunning = true;

            //Console.WriteLine($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} begin to collect run status info!");

            try
            {
                await _semplorestate.WaitAsync();

                using var scope = _service.CreateScope();

                using var _dbContext = scope.ServiceProvider.GetRequiredService<IOpenPasteSpiderDbContext>();

                if (linuxs == null || linuxs.Count == 0)
                {
                    linuxs = new List<int>();
                    //读取服务器
                    await LoadLinuxInfo(_dbContext);
                }

                //30s*60=30分钟检测一次
                if (runtickindex == 60)
                {
                    await LoadLinuxInfo(_dbContext);
                }
                _dbContext.Dispose();
                scope.Dispose();
            }
            catch (Exception exl)
            {
                _logger.LogException(exl, LogLevel.Error);
            }
            finally
            {
                _semplorestate.Release();
            }

            if (runtickindex >= 100)
            {
                runtickindex = 0;
            }
        }



        /// <summary>
        /// 每30分钟重新读取一遍服务器列表
        /// </summary>
        /// <returns></returns>
        private async Task<int> LoadLinuxInfo(IOpenPasteSpiderDbContext _dbContext)
        {

            var linx = await _dbContext.LinuxInfo.Where(x => x.IsEnable).AsNoTracking().ToListAsync();
            if (linx != null && linx.Count > 0)
            {
                linuxs = linx.Select(x => x.Id).ToList();
            }

            return 1;
        }

        #region Linux读取状态

        /// <summary>
        /// 监测服务器
        /// </summary>
        /// <returns></returns>
        private async Task<int> MonitorLinux(IOpenPasteSpiderDbContext _dbContext, int linuxid)
        {
            //var now = DateTime.Now;
            //监控容器运行状态
            var linuxstate = await _actionHelper.GetLinuxState(linuxid);
            if (linuxstate != null)
            {
                var network = await _actionHelper.GetLinuxNetwork(linuxid);

                linuxstate.NetIn = Math.Round(((decimal)network.NetIn / 1024 / 1024), 2);
                linuxstate.NetOut = Math.Round(((decimal)network.NetOut / 1024 / 1024), 2);

                //using var scope = _service.CreateScope();

                //using var _dbContext = scope.ServiceProvider.GetRequiredService<IPasteSpiderDbContext>();

                linuxstate.LinuxId = linuxid;
                linuxstate.DataDate = DateTime.Parse($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss").Substring(0, 18)}0");

                _dbContext.Add(linuxstate);
                //needtoSaveChanges = true;
                await _dbContext.SaveChangesAsync();
                await CollectLinuxHourData(Newtonsoft.Json.JsonConvert.SerializeObject(linuxstate), _dbContext);
                //MointoryLinuxState(Newtonsoft.Json.JsonConvert.SerializeObject(linuxstate), _dbContext);
            }
            else
            {
                _logger.LogError($"read linux info from id:{linuxid} failed!");
            }

            return 1;
        }

        /// <summary>
        /// linux的小时数据 回收有问题，需要修复
        /// </summary>
        private Dictionary<string, LinuxTimeStateDto>? _dichourlinuxcollect;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="linuxstatestr"></param>
        /// <param name="_dbContext"></param>
        private async Task<int> CollectLinuxHourData(string linuxstatestr, IOpenPasteSpiderDbContext _dbContext)
        {

            var date = DateTime.Now;
            var one = Newtonsoft.Json.JsonConvert.DeserializeObject<LinuxTimeStateDto>(linuxstatestr);
            //one.Id = 0;
            if (one != null && one != default)
            {
                var nowkey = $"{date.ToString("yyyyMMddHH")}_{one.LinuxId}";
                if (_dichourlinuxcollect.ContainsKey(nowkey))
                {
                    var item = _dichourlinuxcollect[nowkey];
                    //item.HCPUUsed
                    if (item.HCPUUsed < one.CPUUsed) { item.HCPUUsed = one.CPUUsed; }
                    if (item.LCPUUsed > one.CPUUsed) { item.LCPUUsed = one.CPUUsed; }
                    item.NetOut = one.NetOut;
                    item.NetIn = one.NetIn;
                    item.MemoryFree = one.MemoryFree;
                    item.MemoryTotal = one.MemoryTotal;
                    item.MemoryUsed = one.MemoryUsed;
                    //item.HMemeoryUsed = one.MemoryUsed;
                    if (item.HMemeoryUsed < one.MemoryUsed) { item.HMemeoryUsed = one.MemoryUsed; }
                    if (item.LMemoryUsed > one.MemoryUsed) { item.LMemoryUsed = one.MemoryUsed; }
                }
                else
                {
                    _dichourlinuxcollect.Add(nowkey, one);
                    var ago = date.AddHours(-1);
                    var agekey = $"{ago.ToString("yyyyMMddHH")}_{one.LinuxId}";
                    if (_dichourlinuxcollect.ContainsKey(agekey))
                    {
                        var val = _dichourlinuxcollect[agekey];
                        if (val != null && val != default)
                        {
                            val.Id = 0;
                            val.DataDate = DateTime.Parse(val.DataDate.ToString("yyyy/MM/dd HH:00:00"));
                            _dbContext.Add(_objectMapper.Map<LinuxTimeStateDto, LinuxTimeState>(val));
                            await _dbContext.SaveChangesAsync();
                        }
                        _dichourlinuxcollect.Remove(agekey);
                    }
                }
            }
            return 1;
        }

        #endregion

        #region Container读取状态

        /// <summary>
        /// Container的小时数据 回收有问题，需要需求，可能会跳过回收
        /// </summary>
        private Dictionary<string, DockerTimeStateDto>? _dichourcontainercollect;
        /// <summary>
        /// Container的小时报表
        /// </summary>
        private async Task<int> CollectContainerHourData(string dockertimestatestr, IOpenPasteSpiderDbContext _dbContext)
        {
            //Console.WriteLine
            var date = DateTime.Now;
            var one = Newtonsoft.Json.JsonConvert.DeserializeObject<DockerTimeStateDto>(dockertimestatestr);
            //one.Id = 0;
            if (one != null && one != default)
            {
                var nowkey = $"{date.ToString("yyyyMMddHH")}_{one.DockerName}_{one.LinuxId}";
                if (_dichourcontainercollect.ContainsKey(nowkey))
                {
                    var item = _dichourcontainercollect[nowkey];
                    if (item.HCPU < one.CPUUsed) { item.HCPU = one.CPUUsed; }
                    if (item.LCPU > one.CPUUsed) { item.LCPU = one.CPUUsed; }
                    item.NetOut = one.NetOut;
                    item.NetIn = one.NetIn;
                    if (item.HCPU < one.CPUUsed) { item.HCPU = one.CPUUsed; }
                    if (item.LCPU > one.CPUUsed) { item.LCPU = one.CPUUsed; }
                    if (item.HMemory < one.MemoryUsed) { item.HMemory = one.MemoryUsed; }
                    if (item.LMemory > one.MemoryUsed) { item.LMemory = one.MemoryUsed; }
                }
                else
                {
                    _dichourcontainercollect.Add(nowkey, one);
                    var ago = date.AddHours(-1);
                    var agekey = $"{ago.ToString("yyyyMMddHH")}_{one.DockerName}_{one.LinuxId}";
                    if (_dichourcontainercollect.ContainsKey(agekey))
                    {
                        var val = _dichourcontainercollect[agekey];
                        if (val != null && val != default)
                        {
                            val.Id = 0;
                            val.DataDate = DateTime.Parse(val.DataDate.ToString("yyyy/MM/dd HH:00:00"));
                            _dbContext.Add(_objectMapper.Map<DockerTimeStateDto, DockerTimeState>(val));
                            await _dbContext.SaveChangesAsync();
                        }
                        _dichourcontainercollect.Remove(agekey);
                    }
                }
            }
            return 1;
        }

        /// <summary>
        /// 监听Docker Container服务
        /// </summary>
        /// <returns></returns>
        private async Task<int> MonitorService(IOpenPasteSpiderDbContext _dbContext, int linuxid)
        {
            //using (var scope = _service.CreateScope())
            //{
            //    using (var _dbContext = scope.ServiceProvider.GetRequiredService<IPasteSpiderDbContext>())
            //    {
            var _containerrunlist = await _actionHelper.ReadDockerStatsAsync(linuxid);
            if (_containerrunlist != null && _containerrunlist.Count > 0)
            {
                var listadd = new List<DockerState>();
                var daynow = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
                var daydate = DateTime.Parse($"{daynow.Substring(0, daynow.Length - 1)}0");
                foreach (var item in _containerrunlist)
                {

                    var one = new DockerState()
                    {
                        CPUUsed = (decimal)item.cpuval,
                        DataDate = daydate,
                        DockerName = item.container,
                        LinuxId = linuxid,
                        MemoryUsed = (decimal)item.memoryval,
                        PIDSNum = item.pidval,
                        NetIn = (decimal)item.netinval,
                        NetOut = (decimal)item.netoutval
                    };

                    if (!String.IsNullOrEmpty(item.container))
                    {
                        var strs = item.container.Split('_');
                        if (strs.Length == 3)
                        {
                            int.TryParse(strs[1], out var apid);
                            if (apid > 0)
                            {
                                one.ServiceId = apid;
                            }
                            one.ModelCode = strs[0];
                        }
                    }
                    one.AppShortId = item.idcode;

                    await CollectContainerHourData(Newtonsoft.Json.JsonConvert.SerializeObject(one), _dbContext);

                    //await MointoryServiceState(Newtonsoft.Json.JsonConvert.SerializeObject(one), _dbContext);

                    listadd.Add(one);
                }

                await _dbContext.AddRangeAsync(listadd.ToArray());
                await _dbContext.SaveChangesAsync();

                //#region 检查服务的运行数量是否满足条件

                //await CheckRunLimitNumber(linuxid, listadd, _dbContext);

                //#endregion
            }
            else
            {
                _logger.LogWarning("没有监测到在运行的docker ... .. .");
            }

            //    }
            //}
            //_logger.LogWarning($"size of dbcontext is{Marshal.SizeOf(_dbContext)}");

            return 1;
        }

        #endregion
    }
}
